﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Drawing;
using Silvermoon.UI;
using Silvermoon.Windows;
using Silvermoon.Shapes;

namespace Silvermoon.Controls
{
    /// <summary>
    /// Control which can contain up to 5 items layouted vertically stacked. The control can appear expanded, where only the selected item is shown, or
    /// expanded, where all available items are shown. the height varies between expanded and collapsed to fit only one or all items.
    /// </summary>
    public class DropDownBox : CompositeControlBase, IItemHost
    {
        #region fields

        private List<Item> items = new List<Item>(5);
        private IList itemsSource;
        private ItemEventArgs itemArgs = new ItemEventArgs();
        private int selectedIndex = -1;
        private bool isExpanded;
        private float expansion = 0f;
        private int itemHeight = 80;

        #endregion
        #region ctor

        static DropDownBox()
        {
            VisualStateManager.RegisterClassManager(typeof(DropDownBox), DropDownBoxManagers.Default);
        }

        public DropDownBox()
            : base()
        {
        }

        protected override void Dispose(bool dispose)
        {
            ItemsSource = null;
            base.Dispose(dispose);
        }

        #endregion
        #region props

        public static PropertyAccessor<DropDownBox, float> ExpansionProperty = PropertyAccessor.Register<DropDownBox, float>("Expansion",
            c => c.Expansion, (c, v) => c.Expansion = v);

        public override Size Size
        {
            get
            {
                Size size = base.Size;
                size.Height = GetHeight(expansion);
                return size;
            }
            set
            {
                base.Size = value;
            }
        }


        public int ItemHeight
        {
            get { return itemHeight; }
            set
            {
                if (itemHeight != value)
                {
                    int oldValue = itemHeight;
                    itemHeight = value;
                    OnItemHeightChanged(oldValue);
                }
            }
        }

        protected float Expansion
        {
            get { return expansion; }
            set
            {
                if (expansion != value)
                {
                    float oldValue = expansion;
                    expansion = value;
                    OnExpansionChanged(oldValue);
                }
            }
        }

        public bool IsExpanded
        {
            get { return isExpanded; }
            set
            {
                if (isExpanded != value)
                {
                    isExpanded = value;
                    OnExpandedChanged();
                }
            }
        }

        public IList ItemsSource
        {
            get { return itemsSource; }
            set
            {
                if (itemsSource != value)
                {
                    IList oldValue = itemsSource;
                    itemsSource = value;
                    OnItemsSourceChanged(oldValue);
                }
            }
        }

        public IList<Item> Items { get { return items; } }

        public int Count { get { return itemsSource != null ? itemsSource.Count : 0; } }

        public int SelectedItemIndex
        {
            get { return selectedIndex; }
            set
            {
                if (selectedIndex != value)
                {
                    int oldValue = selectedIndex;
                    selectedIndex = value;
                    OnSelectedItemChanged(oldValue);
                }
            }
        }

        public bool IsDynamicItemHeightSupported { get { return false; } }

        public Item SelectedItem
        {
            get { return selectedIndex >= 0 ? items[selectedIndex] : null; }
            set
            {
                SelectedItemIndex = value != null ? items.IndexOf(value) : -1;
            }
        }

        #endregion
        #region methods


        protected virtual void OnItemHeightChanged(int oldValue)
        {
        }

        protected virtual void OnExpansionChanged(float oldValue)
        {
            Item selectedItem = SelectedItem;
            Invalidate(InvalidationFlag.LayoutAppearance | InvalidationFlag.Size);
            OnSizeChanging(new Size(Width, GetHeight(oldValue)));
            if (expansion == 1f) BringIntoView(IsInitialized);
        }

        private int GetHeight(float expansion)
        {
            int max = Math.Max(1, Count);
            return (int)(ItemHeight * (max - 1) * expansion) + ItemHeight;
        }

        protected override Silvermoon.Shapes.Shape CreateDefaultBackground()
        {
            return new RectBorderFillShape { BorderColor = PhoneColors.Black, Color = Color.White };
        }

        protected virtual void OnSelectedItemChanged(int oldIndex)
        {
            if (oldIndex >= 0)
            {
                Item item = items != null ? items[oldIndex] : null;
                if (item != null) item.IsSelected = false;
            }
            Invalidate(InvalidationFlag.Appearance);
        }

        protected virtual void OnExpandedChanged()
        {
            SetState(isExpanded ? "Expanded" : "Collapsed");
        }

        protected virtual void OnItemsSourceChanged(IList oldValue)
        {
            DisposeRealizedItems();
            if (itemsSource != null)
            {
                if (itemsSource.Count > 5) throw new ArgumentOutOfRangeException("Limit of 5 items exceeded.");
                foreach (var value in itemsSource)
                {
                    items.Add(null);
                }
            }
            selectedIndex = 0;
            Invalidate(InvalidationFlag.Layout);
        }

        private void DisposeRealizedItems()
        {
            VisibleControls.Clear();
            foreach (var item in items)
            {
                if (item != null)
                {
                    item.Release();
                    item.Dispose();
                }
            }
            items.Clear();
        }

        protected virtual Item OnGetVirtualItem(int index, object data)
        {
            Item vItem = data as Item;
            if (vItem != null)
            {
                return vItem;
            }

            if (GetVirtualItem != null)
            {
                ItemEventArgs e = CreateItemEventArgs(index, null, data, this);
                GetVirtualItem(this, e);
                return e.Item;
            }
            else
            {
                return null;
            }
        }

        private ItemEventArgs CreateItemEventArgs(int index, Item item, object value, IItemHost host)
        {
            itemArgs.Index = index;
            itemArgs.Item = item;
            itemArgs.Value = value;
            itemArgs.Host = host;
            return itemArgs;
        }

        protected override void OnInitialized()
        {
            base.OnInitialized();
        }

        protected override void ApplyStyle()
        {
            base.ApplyStyle();
            if (textColor.IsEmpty) DropDownStyles.Default.Apply(this);
            OnExpandedChanged();
        }


        protected override void OnLayout()
        {
            base.OnLayout();
            if (CheckFlag(InvalidationFlag.Appearance)) ChangeAppearance();
            if (CheckFlag(InvalidationFlag.Layout | InvalidationFlag.Size)) LayoutItems();
        }

        private void ChangeAppearance()
        {
            if (SelectedItem != null) SelectedItem.IsSelected = isExpanded;
        }

        protected virtual void LayoutItems()
        {
            VisibleControls.Clear();
            if (Count > 0)
            {
                int top = GetTop();
                int count = Count;
                for (int index = 0; index < count; index++)
                {
                    Rectangle bounds = new Rectangle(0, top, Width, itemHeight);
                    if (IsItemVisible(bounds))
                    {
                        Item item = items[index];
                        if (item == null)
                        {
                            item = OnGetVirtualItem(index, itemsSource[index]);
                            items[index] = item;
                            VisibleControls.Add(item);
                            item.Attach(index, itemsSource[index], this);
                        }
                        else
                        {
                            VisibleControls.Add(item);
                        }
                        item.Bounds = bounds;
                    }
                    top += ItemHeight;
                    if (top >= Height) break;
                }
            }
            Invalidate(InvalidationFlag.Layout);
        }

        private bool IsItemVisible(Rectangle rect)
        {
            if (rect.Bottom < 0) return false;
            return rect.Top < Height;
        }

        private int GetTop()
        {
            int index = Math.Max(0, selectedIndex);
            float y = -(index * ItemHeight * (1f - expansion));
            return (int)(y - 0.5f);
        }


        public override void OnLostFocus()
        {
            base.OnLostFocus();
            IsExpanded = false;
        }

        #endregion
        #region events

        public event EventHandler<ItemEventArgs> ItemAttached;

        /// <summary>
        /// Occurs when a ListViewItem needs to be accessed.
        /// </summary>
        public event EventHandler<ItemEventArgs> GetVirtualItem;

        #endregion
        #region IItemHost Members


        public void SetItemHeight(int index, int height)
        {
            throw new NotImplementedException();
        }

        public void OnTapItem(Item item)
        {
            if (!IsExpanded) IsExpanded = true;
            else
            {
                IsExpanded = false;
                SelectedItemIndex = item.ItemIndex;
            }
        }

        public void BringItemIntoView(int itemIndex, bool animated)
        {
            if (selectedIndex != itemIndex) IsExpanded = true;
            BringIntoView(animated);
        }

        public void BringItemRectIntoView(int itemIndex, Rectangle rect, bool animated)
        {
            BringItemIntoView(itemIndex, animated);
        }

        public bool IsItemSelected(Item item)
        {
            return IsExpanded ? item == SelectedItem : false;
        }


        public Item GetRealizedItem(int index)
        {
            if (index < 0 || index >= items.Count) throw new ArgumentOutOfRangeException("index");
            return items[index];
        }

        #endregion
    }
}
